import { _decorator, Component, instantiate, Node, Prefab, SpriteFrame } from 'cc';
import { ApiMsgEnum, EntityTypeEnum, IClientInput, InputTypeEnum } from '../Common';
import { IMsgClientSync, IMsgServerSync } from '../Common/Msg';
import { ActorManager } from '../Enity/Actor/ActorManager';
import { BulletManager } from '../Enity/Bullet/BulletManager';
import { EventEnum, PrefabPathEnum, TexturePathEnum } from '../Enum';
import DataManager from '../Global/DataManager';
import EventManager from '../Global/EventManager';
import { NetworkManager } from '../Global/NetworkManager';
import { ObjectPoolManager } from '../Global/ObjectPoolManager';
import { ResourceManager } from '../Global/ResourceManager';
import { JoyStickManager } from '../UI/JoyStickManager';
import { deepClone } from '../Utils';
const { ccclass } = _decorator;

@ccclass("BattleManager")
export class BattleManager extends Component {

    private stage: Node
    private ui: Node
    private shouldRender: boolean = false;
    private penddingInputs: IMsgClientSync[] = [];
    protected onLoad(): void {
    }

    async start() {
        this.clearGame();
        await this.loadRes()
        this.initGame();
    }

    initGame() {
        DataManager.Instance.jm = this.ui.getComponentInChildren(JoyStickManager);
        this.shouldRender = true;
        this.initMap();
        EventManager.Instance.on(EventEnum.ClientSync, this.handleClientSync, this)
        NetworkManager.Instance.listenMsg(ApiMsgEnum.MsgServerSync, this.handleServerSync, this)
    }

    clearGame() {
        DataManager.Instance.stage = this.stage = this.node.getChildByName("Stage")
        this.ui = this.node.getChildByName("UI")
        this.stage.destroyAllChildren();
        EventManager.Instance.off(EventEnum.ClientSync, this.handleClientSync, this)
        NetworkManager.Instance.unlistenMsg(ApiMsgEnum.MsgServerSync, this.handleServerSync, this)
    }

    async loadRes() {
        let list = []
        for (const type in PrefabPathEnum) {
            const p = ResourceManager.Instance.loadRes(PrefabPathEnum[type], Prefab).then((prefab) => {
                DataManager.Instance.prefabs.set(type, prefab);
            })
            list.push(p);
        }

        for (const type in TexturePathEnum) {
            const p = ResourceManager.Instance.loadDir(TexturePathEnum[type], SpriteFrame).then((spriteFrames) => {
                DataManager.Instance.textureMap.set(type, spriteFrames);
            })
            list.push(p);
        }
        await Promise.all(list);
    }

    initMap() {
        const prefab = DataManager.Instance.prefabs.get(EntityTypeEnum.Map);
        const node = instantiate(prefab);
        this.stage.addChild(node);
    }

    update(dt) {
        if (!this.shouldRender) {
            return;
        }
        this.render();
        this.tick(dt);
    }

    tick(dt) {
        this.tickActor(dt);

        // DataManager.Instance.applyInput({
        //     type: InputTypeEnum.TimePast,
        //     dt: dt
        // })
    }

    tickActor(dt) {
        for (const data of DataManager.Instance.state.actors) {
            const { id } = data;
            let actorManager = DataManager.Instance.actorMap.get(id);
            actorManager.tick(dt);
        }
    }

    render() {
        this.renderActor();
        this.renderBullet();
    }

    async renderActor() {
        for (const data of DataManager.Instance.state.actors) {
            const { id, type } = data;
            let actorManager = DataManager.Instance.actorMap.get(id);
            if (!actorManager) {
                const prefab = DataManager.Instance.prefabs.get(type);
                const node = instantiate(prefab);
                this.stage.addChild(node);
                actorManager = node.addComponent(ActorManager);
                DataManager.Instance.actorMap.set(id, actorManager);
                actorManager.init(data);
            } else {
                actorManager.render(data)
            }
        }
    }

    async renderBullet() {
        for (const data of DataManager.Instance.state.bullets) {
            const { id, type } = data;
            let bulletManager = DataManager.Instance.bulletMap.get(id);
            if (!bulletManager) {
                const node = ObjectPoolManager.Instance.get(type);
                bulletManager = node.addComponent(BulletManager);
                DataManager.Instance.bulletMap.set(id, bulletManager);
                bulletManager.init(data);
            } else {
                bulletManager.render(data);
            }
        }
    }

    handleClientSync(input: IClientInput) {
        const msg = {
            frameId: DataManager.Instance.frameId++,
            input
        }
        NetworkManager.Instance.sendMsg(ApiMsgEnum.MsgClientSync, msg)

        //帧同步预测
        if (input.type === InputTypeEnum.ActorMove) {
            this.penddingInputs.push(msg);
            DataManager.Instance.applyInput(input);
        }
    }

    handleServerSync(data: IMsgServerSync) {
        //帧同步回滚
        DataManager.Instance.state = DataManager.Instance.lastState;
        const { inputs } = data;
        for (const input of inputs) {
            DataManager.Instance.applyInput(input)
        }

        DataManager.Instance.lastState = deepClone(DataManager.Instance.state);

        this.penddingInputs = this.penddingInputs.filter(msg => msg.frameId > data.lastFrameId)
        for (const msg of this.penddingInputs) {
            DataManager.Instance.applyInput(msg.input)
        }
    }
}